﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RbmlVisualizer.Data;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections;

namespace RbmlVisualizer.Parser
{
    public abstract class TextParser : IParser
    {
        //Properties.
        protected bool bStartUMLFound { get; set; }
        protected bool bEndUMLFound { get; set; }
        protected bool bOpenCurly { get; set; }
        protected ClassDiagram cdClassDiagram { get; set; }
        protected Entity entity { get; set; }
        protected ArrayList alLinesInFile { get; set; }

        protected TextParser()
        {
            this.bStartUMLFound = false;
            this.bEndUMLFound = false;
            this.bOpenCurly = false;
        }

        public ClassDiagram ParseDiagramFromFile(Uri filename, string fileType)
        {
            this.bStartUMLFound = false;
            this.bEndUMLFound = false;
            this.bOpenCurly = false;
            this.cdClassDiagram = new ClassDiagram(fileType);

            ReadFile(filename);
            FillLinkEntities();
            return this.cdClassDiagram;
        }

        public abstract ClassDiagram ParseDiagramFromFile(Uri filename);

        //Fill the entity information in the relationships with the 
        //entity information gathered from the class declarations
        protected void FillLinkEntities()
        {
            foreach (Data.Link.Link link in this.cdClassDiagram.Links)
            {
                link.EntityOne = this.cdClassDiagram.Entities.First(e => e.Uid == link.EntityOne.Uid);
                link.EntityTwo = this.cdClassDiagram.Entities.First(e => e.Uid == link.EntityTwo.Uid);
            }
        }

        protected ArrayList ReadFile(Uri filename)
        {
            ArrayList alLinesFromFile = new ArrayList();
            try
            {
                using (StreamReader sr = new StreamReader(filename.LocalPath))
                {
                    String line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        alLinesFromFile.Add(line);
                        //RegExLine(line);
                    }
                }
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Logging.LoggingService.Instance.WriteException(e,"Issue with the file");
            
            }

            return alLinesFromFile;
        }

        private void RegExLine(string line)
        {
            //try and read @startuml
            if(!this.bStartUMLFound)
                FindStartUML(line);
            //read the PlantUML block between @startuml and @enduml
            if (this.bStartUMLFound && !this.bEndUMLFound)
            {
                //try and read @enduml
                FindEndUML(line);
                ReadPlantUML(line);
            }

        }

        private void ReadPlantUML(string line)
        {
            Data.Link.Link link;
            EntityPart entityPart;

            if (this.bEndUMLFound)
                return;
            else
            {
                if (!bOpenCurly)
                {
                    #region Relationships
                    string pattern = @"(?<EntityOne>\""(?<EntityOneID>[^""\r\n]*)\"")\s*(?<MultiplicityOne>\""(?<MultiOneValue>[^""\r\n]*)\"")?\s+(?<LinkType>(?<LinkEndType1>(o|\*|\<(\|)?)?)(?<LinkStyle>(--|\.\.))(?<LinkEndType2>(o|\*|(\|)?\>)?))\s+(?<MultiplicityTwo>\""(?<MultiTwoValue>[^""\r\n]*)\"")?\s*(?<EntityTwo>\""(?<EntityTwoID>[^""\r\n]*)\"")(?<RelationshipLabel>\s*\:\s*(?<Label>[^\r\n]*))?";
                    Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
                    MatchCollection matches = rgx.Matches(line);

                    if (matches.Count > 0)
                    {
                        link = new Data.Link.Link();
                        foreach (Match match in matches)
                        {
                            GroupCollection groups = match.Groups;
                            link.EntityOne = new Entity();
                            link.EntityOne.Uid = groups["EntityOneID"].Value.Trim();
                            
                            if (this.cdClassDiagram.ClassDiagramType == ClassDiagramType.Uml)
                                link.MultiplicityOne = GetMultiplicity(groups["MultiOneValue"].Value.Trim());
                            else
                                link.LinkEndLabel1 = GetLinkEndLabel(groups["MultiOneValue"].Value.Trim());

                            link.LinkEndType1 = GetLinkEndType(groups["LinkEndType1"].Value.Trim());
                            link.LinkEndType2 = GetLinkEndType(groups["LinkEndType2"].Value.Trim());
                            if (this.cdClassDiagram.ClassDiagramType == ClassDiagramType.Uml)
                                link.MultiplicityTwo = GetMultiplicity(groups["MultiTwoValue"].Value.Trim());
                            else
                                link.LinkEndLabel2 = GetLinkEndLabel(groups["MultiTwoValue"].Value.Trim());
                            link.EntityTwo = new Entity();
                            link.EntityTwo.Uid = groups["EntityTwoID"].Value.Trim();
                            link.Label = groups["Label"].Value.Trim();
                        }
                        this.cdClassDiagram.Links.Add(link);
                        //I have matched a relationship line, just return.
                        return;
                    }
                    #endregion

                    #region Class Declaration (with Open Curly)
                    pattern = @"(?<EntityType>(interface|enum|abstract\s+class|abstract|class))\s+(?<Entity>\""(?<EntityID>[^""\r\n]*)\"")\s+(?<OpenCurly>{)\s*";
                    rgx = new Regex(pattern, RegexOptions.IgnoreCase);
                    matches = rgx.Matches(line);

                    if (matches.Count > 0)
                    {
                        this.entity = new Entity();
                        this.entity.EntityParts = new List<IEntityPart>();
                        foreach (Match match in matches)
                        {
                            GroupCollection groups = match.Groups;
                            this.entity.EntityType = GetEntityType(groups["EntityType"].Value.Trim());
                            this.entity.Uid = groups["EntityID"].Value.Trim();
                            this.bOpenCurly = true;
                        }
                        //I have matched a class declaration with an open curly brace, return out.
                        return;
                    }
                    #endregion
                }
                else //we have an open curly brace, so get the methods or find the closing curly brace
                {
                    #region Entity Methods or Fields
                    //string pattern = @"(?<MemberModifier>^(-|#|~|\+)\s+\b)(?<MemberID>\w+)\s*(?<ArgumentList>(?<OpenParen>\()(?<Argument>(?<Seperator>,\s*)?(?<Type>\w+)(?<Name>\s+\w+)?)*(?<CloseParen>\)))?";
                    string pattern = @"(?<FieldOrMember>[^\r\n}]+)";
                    Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
                    MatchCollection matches = rgx.Matches(line);

                    if (matches.Count > 0)
                    {
                        entityPart = new EntityPart();
                        foreach (Match match in matches)
                        {
                            GroupCollection groups = match.Groups;
                            entityPart.Value = groups["FieldOrMember"].Value.Trim();
                        }
                        this.entity.EntityParts.Add(entityPart);
                        //I have matched a feild or method line, just return.
                        return;
                    }
                    #endregion

                    #region Class Declaration (with Open Curly)
                    pattern = @"(?<CloseCurly>^})";
                    rgx = new Regex(pattern, RegexOptions.IgnoreCase);
                    matches = rgx.Matches(line);

                    if (matches.Count > 0)
                    {
                        foreach (Match match in matches)
                        {
                            GroupCollection groups = match.Groups;
                            this.bOpenCurly = false;
                        }
                        this.cdClassDiagram.Entities.Add(this.entity);
                        //I have matched close curly set open curly to false and retrn out.
                        return;
                    }
                    #endregion
                }

            }
        }

        protected EntityType GetEntityType(string sEntityType)
        {
            string upperEntityType = sEntityType.ToUpper();
            switch (upperEntityType)
            {
                case "ABSTRACT":
                    return EntityType.Abstract;
                case "ABSTRACT CLASS":
                    return EntityType.Abstract;
                case "CLASS":
                    return EntityType.Class;
                case "ENUM":
                    return EntityType.Enum;
                case "INTERFACE":
                    return EntityType.Interface;
                default:
                    return EntityType.None;
            }
        }

        protected Data.Link.LinkEndType GetLinkEndType(string sLinkEndType)
        {
            switch (sLinkEndType)
            {
                case "o":
                    return Data.Link.LinkEndType.Agregation;
                case "*":
                    return Data.Link.LinkEndType.Composition;
                case "<":
                case ">":
                    return Data.Link.LinkEndType.Arrow;
                case "<|":
                case "|>":
                    return Data.Link.LinkEndType.Extends;
                default:
                    return Data.Link.LinkEndType.None;
            }
        }

        protected Multiplicity GetMultiplicity(string sMultiplicity)
        {
            return Multiplicity.None;
        }

        protected string GetLinkEndLabel(string label)
        {
            char[] myChar = {'{','}'};

            var temp = label.TrimEnd(myChar);
            label = temp.TrimStart(myChar);

            return label;
        }

        protected bool FindEndUML(string line)
        {
            string pattern = @"^@endUML\b";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                this.bEndUMLFound = true;
                return true;
            }
            this.bEndUMLFound = false;
            return false;
        }

        protected bool FindStartUML(string line)
        {
            string pattern = @"^@startUML\b";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                this.bStartUMLFound = true;
                return true;
            }
            this.bStartUMLFound = false;
            return false;
        }

    }
}
